import io
import sys
import chardet
from typing import Annotated, List
from fastapi import Depends, APIRouter, Header, Query, Form, Body, File, UploadFile
from fastapi.responses import Response
# from fastapi_utils.inferring_router import InferringRouter
from log import log
from minio.commonconfig import Tags, CopySource, REPLACE
from fastapi_utils.cbv import cbv
from starlette.status import HTTP_403_FORBIDDEN, HTTP_404_NOT_FOUND
from utility.utility import json_result, json_result_reps
from .services import OssBaseHandler, minioCfg
from minio.error import InvalidResponseError, MinioException

# router = InferringRouter()  # Step 1: Create a router
router = APIRouter()


def get_oss_handler(token: Annotated[str | None, Header(alias="X-Access-Token")] = None,
                    user_agent: Annotated[str | None, Header()] = None,
                    bucket_name: str = Query(alias="bucketName", default='contract'),
                    dirname: str = Query(alias="bizPath", default=''),
                    ) -> OssBaseHandler:
    log.info("token", token)
    log.info("user_agent", user_agent)
    oss = OssBaseHandler(dirname, bucket_name)
    try:
        yield oss
    finally:
        del oss


class CommonQueryParams:
    def __init__(self, *,
                 filename: str = Form(default=None) or Query(default=None),
                 filePath: str = Form(default=None) or Query(default=None),
                 ):
        self.filename = filename
        self.filePath = filePath


@cbv(router)
class OssCBV:
    # 第三步：将依赖项设置为类属性，属性值为Depends类型；单独的依懒的变量，和函数参数一样，都能获取url参数
    oss: OssBaseHandler = Depends(get_oss_handler)

    def __updateMemo(self, object_name, metadata):
        try:
            tmp_object_name = object_name + "_update_metadata_tmp"
            tmp_obj_res = self.oss.client.copy_object(
                self.oss.bucket_name, tmp_object_name, CopySource(self.oss.bucket_name, object_name))
            print("finish copying to tmp", self.oss.bucket_name,
                  object_name + "->" + tmp_object_name, tmp_obj_res)
            print("begin copying tmp to origin with additional metadata")
            # self.client.remove_object(self.bucketName, object_name)
            org_obj_res = self.oss.client.copy_object(
                self.oss.bucket_name, object_name,
                CopySource(self.oss.bucket_name, tmp_object_name),
                metadata=metadata, metadata_directive=REPLACE)
            print(org_obj_res)
            self.oss.client.remove_object(self.oss.bucket_name, tmp_object_name)
            print("remove tmp-obj")
        except Exception as e:
            print(e)
            self.oss.result['code'] = 500
            self.oss.result['success'] = False
            self.oss.result['message'] = f'error: {e}'
        return self.oss.result

    @router.get("/")
    def get_item(self):
        return json_result_reps(
            data=self.oss.listObjects()
        )

    @router.get("/list")
    def get_item_non_recursive(self):
        return json_result_reps(
            data=self.oss.listObjects(recursive=False)
        )

    @router.post("/")
    async def create_item(self, id_client: str = Form(default=None), memo: str = Form(default=''), config: str = Form(default=''), commons: CommonQueryParams = Depends(),
                          bucketName: str | None = Form(default=None), bizPath: str = Form(default=None), file: UploadFile = File(alias='file', default=None)):
        log.info("upload starting...")
        if bucketName:
            self.oss.bucket_name = bucketName
            self.oss.minioInit()
        self.oss.dirname = bizPath
        print(type(config))
        metadata = {"memo": memo.encode(), "config": config.encode(), "id_client": id_client}
        # 有则更新，无则新建
        if file:
            filename, content_type = file.filename, file.content_type
            contents = await file.read()
            filesize = len(contents)
            # 日志
            encoding = chardet.detect(b"memo")
            log.info("encoding: {0}".format(encoding))
            log.info("size: {0} or {1}".format(sys.getsizeof(contents), filesize))
            # print(self.bucketName, f'{dirname}{filename}', data, len(data))
            if filesize > minioCfg.file_limit_size:
                self.oss.result.success = False
                self.oss.result.code = 403
                self.oss.result.message = "文件大小不能100M"
                return self.oss.result
            try:
                # 上传操作，但上传是相同路径文件已经存在，会部分继承metadata信息
                filePath = f'{self.oss.dirname}{filename}'
                log.info(filePath)
                stat = None
                try:
                    # 尝试获取对象的元数据
                    stat = self.oss.client.stat_object(self.oss.bucket_name, filePath)
                except MinioException as err:
                    log.info(err)
                if stat:
                    old_memo = stat.metadata.get('X-Amz-Meta-Memo', '')
                    old_config = stat.metadata.get('X-Amz-Meta-Config', '')
                    if not memo and old_memo:
                        metadata['memo'] = old_memo
                    if not config and old_config:
                        metadata['config'] = old_config
                    log.info(metadata)
                res = self.oss.client.put_object(
                    self.oss.bucket_name, filePath, io.BytesIO(contents), filesize,
                    content_type=content_type, metadata=metadata,
                )
                log.info(res)
            except Exception as e:
                self.oss.result.code = 500
                self.oss.result.success = False
                log.error(e)
        # 非文件上传，但更新memo或其它文字信息操作
        else:
            log.info("filePath:{0}, bucket_name:{1}".format(commons.filePath, self.oss.bucket_name))
            self.__updateMemo(commons.filePath, metadata)
        return self.oss.result

    @router.delete('/')
    def delete_item(self, file_path: str = Query(alias='filePaths')):
        try:
            self.oss.delFiles(file_path)
        except Exception as e:
            print(e)
            self.oss.result.code = 500
            self.oss.result.success = False
        self.oss.result['message'] = 'deleted'
        return self.oss.result

    @router.put('/')
    def delete_items(self, filePaths: List = Body(), bucket_name: str = Body(alias="bucketName", default=None)):
        if bucket_name:
            self.oss.bucket_name = bucket_name
            self.oss.minioInit()
        if filePaths:
            try:
                self.oss.delFiles(filePaths)
            except Exception as e:
                print(e)
                self.oss.result.code = 500
                self.oss.result.success = False
            self.oss.result['message'] = 'all deleted'
        return self.oss.result

    # 其它
    @router.get("/read")
    def read_item(self, filePath: str = Query(alias="filePath")):
        res = self.oss.getObject(filePath, isDownload=False)
        log.info(res['header'])
        return Response(
            content=res['data'],
            headers=res['header']
        )

    @router.get("/download")
    def download_item(self, filepath: str = Query(alias="filePath")):
        res = self.oss.getObject(filepath)
        return Response(
            content=res['data'],
            headers=res['header']
        )

    @router.post('/tag')
    def tag_item(self, memo: str = Body(), filePath: str = Body()):
        try:
            log.info(f"memo: {memo}")
            if memo:
                tags = Tags.new_object_tags()
                tags["memo"] = memo
                tags["User"] = "jsmith"
                self.oss.client.set_object_tags(self.oss.bucket_name, filePath, tags)
        except Exception as e:
            self.oss.result.code = 500
            self.oss.result.success = False
            log.info(e)
